from django.contrib.auth.decorators import login_required
from django.http.response import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from django.template import RequestContext
from django.utils import simplejson
from django.db import transaction
from datetime import datetime
import random, json

from page.models import Page, Tracked
from page.tree_utils import save_tree
from question.models import Question, Answer
from test.models import Test, ProgressCheck, DiagnosticTest
from tree_utils import remove_page_from_tree, insert_page, get_root_pages, is_page_under, find_next, find_previous, tree_to_set
from engonline.models import UserAnswer
import collections
from test.models import ActiveDiagnosticTest
from page.utils import get_active_diagnostic_test


# List all the pages (in a tree structure)
#@login_required # TODO: Fix permissions!
def page(request):
    page = Page.objects.filter(trash=False)
    return render(request, 'page.html', {'page':page})

@login_required
def create(request, parent_id=None):
    """
    Create a new page.
    """
    pages = Page.objects.all()
    if request.method == 'POST':
        text = request.POST['editor']
        title = request.POST['title']
        
        #if the new page is missing a title
        if title == "":
            return render(request, 'create.html', {'parent_id':parent_id, 'pages':pages, 'missing_title':True,})
        
        #Tries to put the page under its parent page
        try:
            parent_page = Page.objects.get(id=parent_id)           
            page = Page(title = title, content = text,
                    creator = request.user, changer = request.user)
            page.save()
            insert_page(parent_page, page)
            request.session['page_saved'] = True

        #If the there is no parent, add it as the last root-sibling
        except Page.DoesNotExist:
            page = Page(title = title, content = text, creator = request.user,
                changer = request.user)
            page.save() 
            insert_page(None, page)

        return redirect('page.views.restructure')
       
    else:
        return render(request, 'create.html', {'parent_id':parent_id, 'pages':pages})

@login_required
def edit(request, page_id):
    """
    Edit a page.
    """
    pages = Page.objects.all()

    #If POST, save new attributes.
    #POST is done by AJAX
    if request.method == 'POST':
        print "post"
        text = request.POST['editor']
        title = request.POST['title']
        print text
        print page_id
        page = get_object_or_404(Page, id=page_id)
        
        #If now title is sent, return error.
        if title == "":
            return HttpResponseBadRequest("missing_title")
        
        page.content = text
        page.title = title
        page.changer = request.user
        page.save()
        print page.content
        return HttpResponse("") 
    #Else, show the edit page
    else:
        page_saved = request.session.get('page_saved', False)
        request.session['page_saved'] = False
        page = get_object_or_404(Page, id=page_id)
        return render(request, 'create.html',{'page':page,  'pages':pages, 'edit_page':page, 'page_saved':page_saved},
                context_instance=RequestContext(request))


def trash_page(request,  page_id):
    """
    Set the trash attribute to true and removes the page from the tree
    """
    try:
        page = Page.objects.get(id=page_id)
        remove_page_from_tree(page)
        page.trash = True
        page.save()
        
        return redirect('page.views.page')
    
    except Page.DoesNotExist:
        return redirect('page.views.page')


def view_page(request, slug_title):
    """
    Display a page
    """
    page = get_object_or_404(Page, slug_title=slug_title)
    next = find_next(page)
    prev = find_previous(page)

    if page.type() is Page:
        return render(request, 'view_page.html', {'page': page, 'next': next, 'prev': prev})
    else:
        return view_test(request, slug_title)

def view_test(request, slug_title):
    """
    Display a test
    """
    # TODO:
    # Handle non JS POST

    test = get_object_or_404(Test, slug_title=slug_title)

    if request.method == "GET":

        next = find_next(test)
        prev = find_previous(test)
        test_type = "Exercise"
        questions_to_show = 0

        questions = test.questions.filter(
            trash=False).order_by('contains__question_order')

        # Get a random subset of questions if test is a rpogress check
        if test.type() is ProgressCheck and request.session.get(
            'viewing_as_student', True):

            progress_check = test.progresscheck
            # Make a list of all ids for the questions related to this test
            all_question_ids = list(questions.values_list(
                'id', flat=True))
            # Shuffle the list
            random.shuffle(all_question_ids)
            # Limit to the number of questions the test should contain
            valid_question_ids = all_question_ids[
                :progress_check.questions_to_show]
            # Filter the QuerySet accordingly
            questions = questions.filter(id__in=valid_question_ids)
            
            questions_to_show = progress_check.questions_to_show
            test_type = "ProgressCheck"
        
        elif test.type() is DiagnosticTest:
            test_type = "DiagnosticTest"
        
        # Save currently selected questions to access them efter POST
        request.session['questions'] = questions
        return render(request, 'view_test.html', {'page': test,
            'questions': questions, 'next': next, 'prev': prev, 
            'test_type':test_type,  'questions_to_show':questions_to_show})

    elif request.method == "POST":

        postDict = request.POST.copy()
        date = datetime.now();
        # Retrieve the questions that were shown with GET
        questions = request.session.get('questions')
        
        # Remove CSRF token
        del postDict['csrfmiddlewaretoken']

        corrected_answers = {}
        for question_id in postDict:
            try:
                # Get question if it is part of the test
                question = questions.get(id=question_id)

            except (Question.DoesNotExist, ValueError):
                # Test does not contain question or id is NaN
                print "Invalid question id: \"{}\"".format(question_id)
                return HttpResponseBadRequest()

            # Save question/answer
            answer = postDict.get(question_id, None)
            UserAnswer.objects.create(
                date = date,
                user = request.user, 
                test = test, 
                question = question, 
                answer = answer)

            # Add if the answer is either correct or incorrect and the info
            # accompying that answer
            corrected_answers[question_id] = question.correct_answer(answer)

        return HttpResponse(json.dumps(corrected_answers), 
            content_type="application/json")

    else:
        return HttpResponseBadRequest()

@login_required
def restructure(request):
    """
    !POST:
        Returns the restructure book view.
    POST:
        Tries to save the new book structure.
        Pages in the old tree which are not present in the new structure will be trashed.
        If the process is unsuccessful, all database changes are rolled back.
    """
    if request.method == 'POST':
        try:
            with transaction.commit_on_success():
                #Get the pages in the current tree
                old_tree_set = tree_to_set()

                #Try to save the new tree structure
                serialization = request.POST.get('serialized-structure')
                save_tree(serialization)

                #Get the pages in the new tree
                new_tree_set = tree_to_set()

                #TODO Refactor to method
                #Trash the pages missing from the new tree
                trashed_pages = list(old_tree_set - new_tree_set)
                for page_id in trashed_pages:
                    page = Page.objects.get(pk=page_id)
                    page.trash = True
                    page.save()

                #Restore the pages which are trashed but in the new tree
                restored_pages = list(new_tree_set - old_tree_set)
                for page_id in restored_pages:
                    page = Page.objects.get(pk=page_id)
                    page.trash = False
                    page.save()

                return HttpResponse(status=200)
        except:
            return HttpResponse(status=400)
    else:
        return render(request, 'restructure.html')

@login_required
def my_progress(request):
    """
    Display my progress
    """
    #Try to get the latest diagnostic test
    try:
        test=get_active_diagnostic_test()
        date = UserAnswer.objects.filter(user=request.user, test=test).latest('date').date
        return render(request, 'my_progress.html', {'results': calculate_tracked_result(request.user, test, date)})
    except UserAnswer.DoesNotExist:
        return render(request, 'my_progress.html')

def calulate_page_result(user, test, date):
    """
    Caclutate a result for every page that is related to the test's questions 
    """
    #Get all user answers
    us = UserAnswer.objects.filter(test=test, date=date, user=user)
    pages = []
    correct=collections.defaultdict(int)
    total=collections.defaultdict(int)
    for u in us:
        if u.question.page is not None:
            page = u.question.page.id
            #Get all pages that is related to a question
            if page not in pages:
                pages.append(page)
            
            total[page] += 1
            if u.question.check_answer(u.answer):
                correct[page] += 1
            else:
                correct[page] += 0

        
    result = []
    for page in pages:
        result.append({'correct':correct[page], 'total':total[page], 'page':page})
    
    return result

def calculate_tracked_result(user, test, date):
    """
    Calculate tracked result
    """
    
    total=collections.defaultdict(long)
    correct=collections.defaultdict(long)
    tracked = Tracked.objects.values('page_id')
    
    for result in calulate_page_result(user, test, date):
        for tr in tracked:
            page = Page.objects.get(id=result['page'])
            if is_page_under(tr['page_id'], page) or tr['page_id'] == page.id:
                total[tr['page_id']] += result['total']
                correct[tr['page_id']] += result['correct']
    
    results = {} 
    for tr in tracked:
        page = Page.objects.get(id=tr['page_id'])
        if total[tr['page_id']] != 0:
            procent = float(correct[tr['page_id']])/float(total[tr['page_id']])*100 
            procent = int(procent+0.5) 
            results[tr['page_id']] = {"tracked":tr['page_id'], 'page':page, 'procent':procent, 'correct':correct[tr['page_id']], 'total':total[tr['page_id']]}
        else: 
            procent = "No questions answered."    
            results[tr['page_id']] = {"tracked":tr['page_id'], 'page':page, 'correct':0, 'total':0}
        
    return results

@login_required
def take_diagnostic(request):
    """
    Take the diagnostic test
    """
    diagnostic_test = get_active_diagnostic_test()
    if diagnostic_test is not None:
        return view_test(request, diagnostic_test.slug_title)
    else:
        #TODO handle no active diagnostic_test
        return page(request)

@login_required    
def set_questions_to_show(request):
    """
    Set number of questions to show in a progress check
    """
    if request.method == 'POST':
        page_id = request.POST['page_id']
        questions = request.POST['questions_to_show']
        pc = get_object_or_404(ProgressCheck, id=page_id)
        pc.questions_to_show = questions
        pc.save()
        return HttpResponseRedirect(pc.get_absolute_url())


    
               
    